package ee.fj.http;

import java.io.BufferedReader;
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.Socket;
import java.util.HashMap;
import java.util.Map;
import java.util.StringTokenizer;
import java.util.logging.Level;
import java.util.logging.Logger;



public class ServerHandler  implements Runnable {
	private static final Logger logger = Logger.getLogger(ServerHandler.class.getName());

	private Response response;
	private Request request;
	private BinderParser binder;

	public ServerHandler(String serverName, Socket client, BinderParser binder) throws IOException {
		this.binder = binder;
		BufferedReader in = new BufferedReader(new InputStreamReader(client.getInputStream()));
		DataOutputStream out = new DataOutputStream(client.getOutputStream());
		String headerLine = in.readLine();
		StringTokenizer tokenizer = new StringTokenizer(headerLine);
		String method = tokenizer.nextToken();
		String httpQueryString = tokenizer.nextToken();
		tokenizer.nextToken(); // HTTP/1.1
		String mPath = null;
		String path = null;
		Map<String, String> params = new HashMap<String, String>();
		String[] subPath = null;
		if (httpQueryString.indexOf('?') < 0) {
			mPath = httpQueryString;
		} else {
			StringTokenizer argsTok = new StringTokenizer(httpQueryString, "?");
			mPath = argsTok.nextToken();
			httpQueryString = argsTok.nextToken();
			if (httpQueryString.indexOf('&') > -1) {
				argsTok = new StringTokenizer(httpQueryString, "&");
				while (argsTok.hasMoreElements()) {
					addParam(params, argsTok.nextToken());
				}
			} else {
				addParam(params, httpQueryString);
			}
		}
		if (mPath == null || mPath.trim().length() == 0 || mPath.trim().equals(Request.DEFAULT_PATH)) {
			path = Request.DEFAULT_PATH;
		} else {
			mPath = mPath.trim();
			if (mPath.startsWith(Request.DEFAULT_PATH)) {
				mPath = mPath.substring(1);
			}
			if (mPath.indexOf('/') > 0) {
				path = mPath.substring(0, mPath.indexOf('/'));
				if (mPath.length() > mPath.indexOf('/') + 1) {
					subPath = mPath.substring(mPath.indexOf('/') + 1).split(Request.DEFAULT_PATH);
				}
			} else {
				path = mPath;
			}
		}
		if (path == null || path.trim().length() == 0){
			path = Request.DEFAULT_PATH;
		}
		path = path.trim();
		try {
			this.response = new Response(out, serverName);
			try {
				this.request = new Request(in, method, path, subPath, params);
			} catch (IOException e) {
				response.getWriter().append("505 ERROR!");
				response.setHeader(true, Response.STATUS.ERROR);
				throw e;
			}
		} catch (Throwable e) {
			close();
			throw e;
		}
	}

	private void addParam(Map<String, String> params, String line) {
		if (line.indexOf('=') > -1) {
			StringTokenizer tokenizer = new StringTokenizer(line, "=");
			params.put(tokenizer.nextToken(), tokenizer.nextToken());
		} else {
			params.put(line, null);
		}
	}

	@Override
	public void run() {
		try {
			try {
				this.binder.handle(request, response);
			} finally {
				this.close();
			}
		} catch (Throwable e) {
			logger.log(Level.SEVERE, e.getMessage(), e);
		}
	}

	public void closeResponse() throws IOException {
		this.response.close();
	}
	public void closeRequest() throws IOException {
		this.request.close();
	}

	public void close() throws IOException {
		try {
			this.closeResponse();
		} finally {
			this.closeRequest();
		}
	}

}
